class BindPoint {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.children = [];
    }
};

class Player {

    constructor(x, y, curSkeleton, skeletons, attacSkeletons) {
        this.x = x;
        this.y = y;
        this.state = 0;
        this.attackState = 0;
        this.skeletons = skeletons;
        this.attacSkeletons = attacSkeletons;
        this.curSkeleton = curSkeleton;
        this.intervalSum = 0;
        this.attackIntervalSum = 0;
        this.moving = false;
        this.timers = {};
        this.timerIndex = 0;
        this.attackTimerId = -1;
        this.attacking = false;
        this.direction = 1;
        this.attackDirection = 1;
    }

    getSkeletons() {
        if (this.attacking) {
            return this.attacSkeletons;
        } else {
            return this.skeletons;
        }
    }

    getSkeletonsLen() {
        return this.getSkeletons().length;
    }

    getState() {
        if (this.attacking) {
            return this.attackState;
        } else {
            return this.state;
        }
    }

    setState(state) {
        if (this.attacking) {
            this.attackState = state;
        } else {
            this.state = state;
        }
    }

    getIntervalSum() {
        if (this.attacking) {
            console.log('this.attackIntervalSum', this.attackIntervalSum);
            return this.attackIntervalSum;
        } else {
            return this.intervalSum;
        }
    }

    setIntervalSum(sum) {
        if (this.attacking) {
            this.attackIntervalSum = sum;
        } else {
            this.intervalSum = sum;
        }
    }

    getDirection() {
        if (this.attacking) {
            return this.attackDirection;
        } else {
            return this.direction;
        }
    }

    draw(ctx) {
        Object.keys(this.curSkeleton.skeleton).forEach((key) => {
            const p = this.curSkeleton.skeleton[key];
            p.children.forEach((s) => {
                ctx.beginPath();
                ctx.moveTo(p.x + this.curSkeleton.x, p.y + this.curSkeleton.y);
                ctx.lineTo(s.x + this.curSkeleton.x, s.y + this.curSkeleton.y);
                ctx.stroke();
                ctx.closePath();
            });
        });
        this.drawHead(ctx);
    }

    drawHead(ctx) {
        const p1 = this.curSkeleton.skeleton.neck1;
        const p2 = this.curSkeleton.skeleton.neck2;
        if (p1 && p2) {   
            const dertX = p1.x - p2.x;
            const dertY = p1.y - p2.y;
            const x = p2.x + 3*dertX + this.curSkeleton.x;
            const y = p2.y + 3*dertY + this.curSkeleton.y;
            const r = Math.sqrt(dertX*dertX + dertY*dertY)*2;
            ctx.beginPath();
            ctx.arc(x, y, r, 0, Math.PI*2, false);
            ctx.stroke();
            ctx.closePath();
        }
    }

    nextState() {
        if (this.getState() == this.getSkeletonsLen() - 1) {
            if (this.attacking) {
                return 1;
            } else {
                return 0;
            }
        } else {
            return this.getState() + 1;
        }
    }

    lastState() {
        if (this.getState() == 0) {
            return this.getSkeletonsLen() - 1;
        } else {
            if (this.attacking && this.getState() == 1) {
                return this.getSkeletonsLen() - 1;
            } else {
                return this.getState() - 1;
            }
        }
    }

    moveToNextState() {
        this.setState(this.nextState());
    }

    moveToLastState() {
        this.setState(this.lastState());
    }

    updateState() {
        if (this.getIntervalSum() >= this.getSkeletons()[this.getState()].interval) {
            this.setIntervalSum(this.getIntervalSum() - this.getSkeletons()[this.getState()].interval);
            this.moveToNextState();
            return true;
        } else if (this.getIntervalSum() < 0) {
            this.setIntervalSum(this.intervalSum + this.getSkeletons()[this.lastState()].interval);
            this.moveToLastState();
            return true;
        }
        return false;
    }

    animation(lastTime) {
        if (!this.attacking && !this.moving) { 
            return ;    
        }
        const interval = Date.now() - lastTime;
        this.setIntervalSum(this.getIntervalSum() + interval*this.getDirection());
        // this.intervalSum += interval * this.direction;
        while (this.updateState()) {
            Object.keys(this.curSkeleton.skeleton).forEach((key) => {
                const p = this.curSkeleton.skeleton[key];
                p.x = this.getSkeletons()[this.getState()].skeleton[key].x;
                p.y = this.getSkeletons()[this.getState()].skeleton[key].y;
            });
            this.x += this.getSkeletons()[this.lastState()].xDis * this.getDirection();
            this.y += this.getSkeletons()[this.lastState()].yDis * this.getDirection();
        }
        let mul = this.getIntervalSum() / this.getSkeletons()[this.getState()].interval;
        console.log('mul', mul);
        Object.keys(this.curSkeleton.skeleton).forEach((key) => {
            const p = this.curSkeleton.skeleton[key];
            const base = this.getSkeletons()[this.getState()].skeleton[key];
            const dertX = this.getSkeletons()[this.nextState()].skeleton[key].x - this.getSkeletons()[this.getState()].skeleton[key].x;
            const dertY = this.getSkeletons()[this.nextState()].skeleton[key].y - this.getSkeletons()[this.getState()].skeleton[key].y;
            p.x = base.x + dertX * mul;
            p.y = base.y + dertY * mul;
        });
        this.curSkeleton.x = this.x + this.getSkeletons()[this.getState()].xDis * mul;
        this.curSkeleton.y = this.y + this.getSkeletons()[this.getState()].yDis * mul;
        // console.log('this.curSkeleton.x ', this.curSkeleton.x);
    }

    move(direction) {
        if (!this.attacking) {
            this.moving = true;
            this.direction = direction;
        }
    }

    stop() {
        this.moving = false;
    }

    resetToCurSkeletonPos() {
        this.x = this.curSkeleton.x;
        this.y = this.curSkeleton.y;
    }

    attack() {
        this.attacking = true;
        this.getSkeletons()[0].copy(this.curSkeleton);
        this.resetToCurSkeletonPos();
        if (this.attackTimerId >= 0) {
            this.unregisterTimer(this.attackTimerId);
        }
        this.attackTimerId = this.registerTimer(2000, () => {
            this.attacking = false;
            this.resetToCurSkeletonPos();
        });
    }

    registerTimer(timeout, cb) {
        const future = Date.now() + timeout;
        this.timerIndex ++;
        this.timerIndex %= 65535;
        this.timers[this.timerIndex] = {
            future,
            cb
        }
        return this.timerIndex;
    }

    unregisterTimer(id) {
        this.timers[id] && delete this.timers[id];
    }

    doTimer() {
        Object.keys(this.timers).forEach((key) => {
            const timer = this.timers[key];
            if (Date.now() >= timer.future) {
                timer.cb && timer.cb();
                delete this.timers[key];
            }
        });
    }
}